import { ActionTypes } from "./utils/actionTypes";
import { isPlainObject } from "./utils/isPlainObject";

/**
 * 尝试验证 reducers 写的是否合理
 * @param {*} reducers 
 */
function assertReducerShape(reducers) {
    // 获取枚举自身属性
    Object.keys(reducers).forEach(key => {
        const reducer = reducers[key];
        let state = reducer(undefined, { type: ActionTypes.INIT });
        if (state === undefined) {
            throw new TypeError("reducers must not return undefined");// reducer 返回不能是 undefined
        }
        state = typeof reducer(undefined, { type: ActionTypes.PROBE_UNKNOWN_ACTION });
        if (state === undefined) {
            throw new TypeError("reducers must not return undefined");
        }
    })
}

/**
 * 将多个 reducer 合并为一个
 * @param {*} reducers 需合并 reducer 状态对象
 * @returns 
 */
export function combineReducers(reducers) {
    // 验证参数为对象
    if (typeof reducers !== "object") {
        throw new TypeError("reducers must be an object"); // 参数不是对象直接抛错
    }
    // 验证参数平面对象
    if (!isPlainObject(reducers)) {
        throw new TypeError("reducers must be a plain object"); // 不是平面对象也抛错
    }
    const reducerKeys = Object.keys(reducers); // 得到对象所有属性名
    // 遍历传入对象属性名过滤掉属性值不是 reducer 函数
    // const finalReducers = {};
    // for (let i = 0; i < reducerKeys.length; i++) {
    //     const key = reducerKeys[i];
    //     if (typeof reducers[key] === 'undefined') {
    //         console.warn(`No reducer provided for key "${key}"`)
    //     }
    //     if (typeof reducers[key] === 'function') {
    //         finalReducers[key] = reducers[key];
    //     }
    // }
    // 等价于上面写法
    const finalReducers = reducerKeys.reduce((a, b) => {
        if (typeof reducers[b] === 'undefined') {
            console.warn(`No reducer provided for key "${b}"`);
        }
        if (typeof reducers[b] === 'function') {
            a[b] = reducers[b];
        }
        return a;
    }, {});
    // 收集验证错误消息
    let shapeAssertionError;
    try {
        // 进行两次验证 reducer 返回结果是否 undefined 也就是判断写的 reducer 是否符合标准不返回 undefined 则标准
        assertReducerShape(finalReducers)
    } catch (e) {
        shapeAssertionError = e  // 收集错误信息
    }
    return (state = {}, action) => {
        // 验证 reducer 失败直接抛错
        if (shapeAssertionError) {
            throw shapeAssertionError
        }
        let hasChanged = false;
        const newState = {};
        for (const key in finalReducers) {
            const reducer = finalReducers[key]; // 获取到对应 reducer 方法
            const previousStateForKey = state[key]; // reducer 对应的状态数据
            const newStateForKey = reducer(previousStateForKey, action); //  reducer 处理后状态数据返回值(reducer对应状态)
            // 再次判断是否更新仓库值是否 undefined
            if (typeof newStateForKey === "undefined") {
                throw new TypeError("reducers must not return undefined");
            }
            newState[key] = newStateForKey;
            hasChanged = hasChanged || newStateForKey !== previousStateForKey; // 判断前后状态是否相同
        }
        hasChanged = hasChanged || Object.keys(finalReducers).length !== Object.keys(state).length;
        return hasChanged ? newState : state;
    }
}